feat: add boot_mode to os_management ResetWriteRequest (closes #58)#59
Merged
Conversation
Model the optional `boot_mode` field on `ResetWriteRequest` so a client can request a reboot into the bootloader (e.g. MCUboot serial recovery) rather than a normal reboot. The field is encoded into the reset command's CBOR map under the key `boot_mode` only when set, matching the existing `force` handling. Values are modeled with a `BootMode(IntEnum)` (`NORMAL = 0`, `BOOTLOADER = 1`) mirroring Zephyr's `enum BOOT_MODE_TYPES` in `include/zephyr/retention/bootmode.h`. Because the server casts the value to a `uint8_t`, the field accepts any value in `[0, 255]` (known values surface as `BootMode` members; the range guard keeps invalid messages unconstructable per the library's contract). This field was added to the SMP OS management group in Zephyr v4.2.0 (zephyrproject-rtos/zephyr#91510), gated by `CONFIG_MCUMGR_GRP_OS_RESET_BOOT_MODE` (depends on `CONFIG_RETENTION_BOOT_MODE`). Unblocks intercreate/smpclient#112 and intercreate/smpmgr#100. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds support for Zephyr’s optional OS-management reset command parameter boot_mode by extending smp.os_management.ResetWriteRequest, enabling clients to request rebooting into a bootloader/recovery mode while preserving existing wire behavior when the field is unset.
Changes:
- Introduces
BootMode(IntEnum)withNORMAL=0andBOOTLOADER=1to represent known reset boot modes. - Adds optional
ResetWriteRequest.boot_modewith validation foruint8range[0, 255]and left-to-right union decoding so0/1deserialize toBootModemembers. - Adds tests covering known enum values, unknown passthrough ints, combined
force+boot_mode, enum-member construction equivalence, and out-of-range validation failures.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
smp/os_management.py |
Adds BootMode enum and boot_mode field on ResetWriteRequest with range validation and union decoding behavior. |
tests/test_os_management.py |
Adds test coverage ensuring correct (de)serialization and validation behavior for the new boot_mode field. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the optional
boot_modefield tosmp.os_management.ResetWriteRequest, so a client can request a reboot into the bootloader (e.g. MCUboot serial recovery) rather than a normal reboot. Resolves #58.BootMode(IntEnum)—NORMAL = 0,BOOTLOADER = 1— mirroring Zephyr'senum BOOT_MODE_TYPESininclude/zephyr/retention/bootmode.h.ResetWriteRequest.boot_modeis encoded into the reset command's CBOR map under the keyboot_modeonly when set, exactly like the existingforcefield.BootModemembers; any other value in[0, 255]is accepted as a plainint(the server casts touint8_t). Out-of-range values (< 0or> 255) raiseValidationError, keeping the library's "impossible to create an invalid SMP message" contract intact.Protocol provenance
I tracked down when this entered the protocol:
boot_modewas added to the SMP OS managementresetcommand in Zephyr v4.2.0 via zephyrproject-rtos/zephyr#91510 (commit0adcf2e, merged 2025-06-18); it is not present in v4.1.0. Server-side it is gated byCONFIG_MCUMGR_GRP_OS_RESET_BOOT_MODE(depends onCONFIG_RETENTION_BOOT_MODE). Theos_mgmt.chandler decodes the CBOR key string"boot_mode"as an unsigned int, validates<= UCHAR_MAX, casts touint8_t, and passes it tobootmode_set().BOOT_MODE_TYPE_BOOTLOADER(1) is what an MCUboot built withCONFIG_BOOT_SERIAL_BOOT_MODEchecks to enter serial recovery on the next boot.Design notes
Union[BootMode, Annotated[int, Field(ge=0, le=255)], None]withunion_mode="left_to_right". This mirrors the existing house pattern atsmp/header.py:103(Annotated[Union[GroupId, UserGroupId, int], Field(union_mode="left_to_right")]). Left-to-right is load-bearing: under the default smart union,0/1would deserialize to plainintinstead ofBootModemembers.boot_modeis unset it is excluded from the CBOR map (exclude_unset/exclude_none), so an empty reset request still serializes toa0.Testing
tests/test_os_management.pycover:boot_mode0/1 →BootModemembers, unknown-but-valid int passthrough,force+boot_modetogether, enum-member construction matching the raw-int payload byte-for-byte, and rejection of-1/256.smp/os_management.py),black/isort/flake8/mypyall clean.🤖 Generated with Claude Code